/*!
* 
* Copyright (c) 2009 by yuqiang @ vcedu
* 
* Description:
*		audiosession, it can send audio and receive audio
*
* ChangeLog:
*		$Date$	$Rev$	$Author$
* 
*/	

#ifndef _AUDIOSESSION_H_
#define _AUDIOSESSION_H_

#include <mipcomponentchain.h>
#include <miperrorbase.h>
#include <miptime.h>
#include <rtptransmitter.h>
#include <string>
#include <list>

#include "dllheader.h"

class RTPSession;
class RTPAddress;
class MIPComponent;
class MIPRTPComponent;
class MIPRTPSynchronizer;
class MIPRTPPacketDecoder;

/** Parameters for an audio session. */
class AudioSessionParams
{
public:

	AudioSessionParams()								
	{ 
		m_highPriority = false;
		m_portbase = 6000; 
		m_acceptOwnPackets = false; 
		m_speexMode = WideBand;
		m_speexIncomingPT = 96;
		m_speexOutgoingPT = 96;

		m_inputMultiplier = 3;
		m_outputMultiplier = 2;

		m_compType = ULaw;
	}
	~AudioSessionParams()							{ }

	/** Returns \c true if the audio threads will receive high priority (only on Win32/WinCE; default: false). */
	bool getUseHighPriority() const							{ return m_highPriority; }

	/** Returns the RTP portbase (default: 5000). */
	uint16_t getPortbase() const							{ return m_portbase; }

	/** Returns \c true if own packets will be accepted (default: \c false). */
	bool getAcceptOwnPackets() const						{ return m_acceptOwnPackets; }

	/** Returns the requested compression/encoding type (default: U-law). */
	CompressionType getCompressionType() const					{ return m_compType; }

	/** Returns the Speex mode (default: WideBand). */
	SpeexBandWidth getSpeexEncoding() const						{ return m_speexMode; }

	/** Incoming packets with this payload type will be interpreted as Speex packets. */
	uint8_t getSpeexIncomingPayloadType() const					{ return m_speexIncomingPT; }

	/** This payload type will be set on outgoing Speex packets. */
	uint8_t getSpeexOutgoingPayloadType() const					{ return m_speexOutgoingPT; }

	/** Returns the size of the input sampling interval, in units of 20ms. */
	int getInputMultiplier() const							{ return m_inputMultiplier; }

	/** Returns the size of the output sampling interval, in units of 20ms. */
	int getOutputMultiplier() const							{ return m_outputMultiplier; }

	/** Sets a flag indicating if high priority audio threads should be used (only on Win32/WinCE). */
	void setUseHighPriority(bool f)							{ m_highPriority = f; }

	/** Sets the RTP portbase. */
	void setPortbase(uint16_t p)							{ m_portbase = p; }

	/** Sets a flag indicating if own packets should be accepted. */
	void setAcceptOwnPackets(bool v)						{ m_acceptOwnPackets = v; }

	/** Sets the compression/encoding type. */
	void setCompressionType(CompressionType t)					{ m_compType = t; }

	/** Sets the Speex encoding mode. */
	void setSpeexEncoding(SpeexBandWidth b)						{ m_speexMode = b; }

	/** This will interpret incoming packets with payload type \c pt as Speex packets. */
	void setSpeexIncomingPayloadType(uint8_t pt)					{ m_speexIncomingPT = pt; }

	/** Sets the payload type for outgoing Speex packets. */
	void setSpeexOutgoingPayloadType(uint8_t pt)					{ m_speexOutgoingPT = pt; }

	/** Sets the input sampling interval to \c m * 20ms. */
	void setInputMultiplier(int m)							{ m_inputMultiplier = m; }

	/** Sets the output sampling interval to \c m * 20ms. */
	void setOutputMultiplier(int m)							{ m_outputMultiplier = m; }

private:
	bool m_highPriority;

	uint16_t m_portbase;
	bool m_acceptOwnPackets;
	SpeexBandWidth m_speexMode;
	int m_inputMultiplier, m_outputMultiplier;
	CompressionType m_compType;
	uint8_t m_speexOutgoingPT, m_speexIncomingPT;
};

/** Creates a VoIP session.
*  This wrapper class can be used to create a VoIP session. Transmitted data
*  will be Speex encoded. Destinations are specified using subclasses of
*  RTPAddress from the JRTPLIB library. Currently, only RTPIPv4Address
*  instances can be specified.
*/
class AudioSession : public MIPErrorBase
{
public:
	AudioSession();
	virtual ~AudioSession();

	/** Initializes the session.
	*  Using this function, the session is initialized.
	*  \param pParams Session parameters.
	*  \param pSync RTP stream synchronizer.
	*  \param pRTPSession Supply your own RTPSession instance with this parameter. In this case,
	*                     the RTPSession instance is not deleted when the audio session is destroyed.
	*                     The session has to be initialized, but the timestamp unit will still be 
	*                     adjusted.
	*/
	bool init(const AudioSessionParams *pParams = 0, MIPRTPSynchronizer *pSync = 0, RTPSession *pRTPSession = 0);

	/** Destroys the session. */
	bool destroy();

	/** Add a destination. */
	bool addDestination(const RTPAddress &addr);

	/** Delete a destination. */
	bool deleteDestination(const RTPAddress &addr);

	/** Clear the destination list. */
	bool clearDestinations();

	/** Returns \c true if multicasting is supported, \c false otherwise. */
	bool supportsMulticasting();

	/** Joins a multicast group. */
	bool joinMulticastGroup(const RTPAddress &addr);

	/** Leaves a multicast group. */
	bool leaveMulticastGroup(const RTPAddress &addr);

	/** Leaves all multicast groups. */
	bool leaveAllMulticastGroups();

	/** Set a receive mode.
	*  Using this function, a receive mode can be specified. Valid receive modes are
	*  \c RTPTransmitter::AcceptAll, \c RTPTransmitter::AcceptSome and \c RTPTransmitter::IgnoreSome.
	*  In the last two cases, packets are accepted or ignored based upon information in the
	*  accept or ignore list. Note that changing the receive mode will cause such a list to be cleared.
	*/
	bool setReceiveMode(RTPTransmitter::ReceiveMode m);

	/** Adds an address to the ignore list. */
	bool addToIgnoreList(const RTPAddress &addr);

	/** Removes an address from the ignore list. */
	bool deleteFromIgnoreList(const RTPAddress &addr);

	/** Clears the ignore list. */
	bool clearIgnoreList();

	/** Adds an address to the accept list. */
	bool addToAcceptList(const RTPAddress &addr);

	/** Deletes an address from the accept list. */
	bool deleteFromAcceptList(const RTPAddress &addr);

	/** Clears the accept list. */
	bool clearAcceptList();

	/**
	*  start send audio chain, so user can receive our audio
	*/
	bool startSendChain();

	/**
	*  stop send audio chain, so user can not receive our audio
	*/
	bool stopSendChain();

	/**
	*  start recieve audio chain, so we can receive user's audio
	*/
	bool startReceiveChain();

	/**
	*  stop recieve audio chain, so we can not receive user' audio
	*/
	bool stopReceiveChain();

	/**
	*  start the audio session
	*/
	bool startSession();

	/**
	*  stop the audio session
	*/
	bool stopSession();

	bool isInit() {return m_init;}

	bool isSessionRunning() {return (m_inputRunning || m_outputRunning);}

	bool isSendRunning() {return m_outputRunning;}

	bool isReceiveRunning() {return m_inputRunning;}

protected:
	/** By overriding this function, you can detect when the input thread has finished.
	*  By overriding this function, you can detect when the input thread has finished.
	*  \param err Flag indicating if the thread stopped due to an error.
	*  \param compName Contains the component in which the error occured.
	*  \param errStr Contains a description of the error.
	*/
	virtual void onInputThreadExit(bool err, const std::string &compName, const std::string &errStr);

	/** By overriding this function, you can detect when the output thread has finished.
	*  By overriding this function, you can detect when the output thread has finished.
	*  \param err Flag indicating if the thread stopped due to an error.
	*  \param compName Contains the component in which the error occured.
	*  \param errStr Contains a description of the error.
	*/
	virtual void onOutputThreadExit(bool err, const std::string &compName, const std::string &errStr);

private:
	class InputChain : public MIPComponentChain
	{
	public:
		InputChain(AudioSession *pAudioSess) : MIPComponentChain("Input chain")		{ m_pAudioSess = pAudioSess; }
	protected:
		void onThreadExit(bool err, const std::string &compName, const std::string &errStr)	{ m_pAudioSess->onInputThreadExit(err, compName, errStr); }
	private:
		AudioSession *m_pAudioSess;
	};

	class OutputChain : public MIPComponentChain
	{
	public:
		OutputChain(AudioSession *pAudioSess) : MIPComponentChain("Output chain")		{ m_pAudioSess = pAudioSess; }
	protected:
		void onThreadExit(bool err, const std::string &compName, const std::string &errStr)	{ m_pAudioSess->onOutputThreadExit(err, compName, errStr); }
	private:
		AudioSession *m_pAudioSess;
	};

	void zeroAll();
	void deleteAll();
	void storeComponent(MIPComponent *pComp);
	void storePacketDecoder(MIPRTPPacketDecoder *pDec);
	void addLink(MIPComponentChain *pChain, MIPComponent **pPrevComp, MIPComponent *pComp, 
		bool feedback = false, uint32_t mask1 = MIPMESSAGE_TYPE_ALL, uint32_t mask2 = MIPMESSAGE_TYPE_ALL);

	bool m_init;
	bool m_inputRunning;
	bool m_outputRunning;

	InputChain *m_pInputChain;
	OutputChain *m_pOutputChain;

	MIPRTPComponent *m_pRTPComp;
	RTPSession *m_pRTPSession;
	bool m_deleteRTPSession;
	std::list<MIPComponent *> m_components;
	std::list<MIPRTPPacketDecoder *> m_packetDecoders;

	friend class InputChain;
	friend class OutputChain;
};


#endif //_AUDIOSESSION_H_